/**
 * 有效性验证插件
 * Author: John Fang 2018-11-22 
 * 
 * @param keepfocus： true不通过验证保持元素处于focus，其他值为不保持focus 
 * 
 * @return true:通过校验 false：没有通过校验。
 *
 * 使用rule属性作为验证规则的key，规则数据采用JSON格式存储，其格式约定如下：
 * nn(notNull) : 可以为空:0; 不能为空:1; 默认为 0
 * vt(valueType): 文本：0;整数：1; 浮点数：2;正则表达式：3;选项：4; 文件名格式：5 ;默认为 0。
 * mx(max): 最大值，文本和正则表达式验证时，最大字符长度；整数和浮点数为最大值;枚举选项：最大数量。不设置表示不限。
 * mn(min): 最小值，文本和正则表达式验证时，最小字符长度；整数和浮点数为最小值;枚举选项：最小数量。不设置表示不限。
 * rg(regular):正则表达式，使用javascript格式。 当vt=3或者5时有效
 * rv(remoteValidation): 远程验证。可以为空， 格式 rule='{...,"rv":{"pr":"permit","ef":["id","email"]}}'
 *                       pr(permit)许可远程验证使用的许可字符串，
 *                       ef(extfield)扩展参数，表单输入元素name数组；
 * 如： rule="{'nn':0,'vt':0,'mx':16,'mn':6}"定义可以为空的长度介于6至16个字符的文本
 *      rule='{"nn":1,"vt":0,"mx":16,"mn":3,"rv":{"pr":"admin:user:add:uniqueID","ef":["user_id","mobile"]}}'不能为空的3-16长度的文本，
 *        需要使用许可"admin:user:add:uniqueID"远程校验并需要附带同一表单的user_id和mobile字段一起提交。
 *
 * 校验顺序：使用断路比较
 * （1）使用 nn 判断是否可以为空。通过进行下一步
 * （2）使用 vt,mx,mn和rg进行格式数值比较。
 * （3）使用 rp向服务器验证数据是否有效。返回JSON数据格式 {'code':'200(有效)/201(无效)','msg':'结果说明'}
 */
jQuery.fn.validation = function(keepfocus){
	_ie = $(this);
	rtxt = '';
	if(_ie.prop('tagName')=='INPUT' && (_ie.prop("type")=='checkbox' || _ie.prop("type")=='radio')){
		_rr = _ie.parents("form").find("input[name='"+_ie.attr("name")+"'][rule]");
		if(_rr.length>0) rtxt = $(_rr[0]).attr("rule");
		//如果有多个rule设置，取第一个rule有效。
	}else{
		rtxt = _ie.attr("rule");
	}
	
	
	if(rtxt && rtxt.length >0){
		try{
		var _r = $.parseJSON(rtxt); //将规则转为json对象。
		}catch(err){
			//如果无法转为json对象抛出异常退出插件。
			_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>校验规则不是JSON格式。</font>");
			return false;
		}
		_n = _ie.attr("name");
		_v = _ie.val();
		
		//不能为空的校验
		if(_r['nn']=='1' && _v==''){
			//必填项目但是没有值
			_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i> 必填项不能为空。</font>");
			if(keepfocus) _ie.focus();
			return false;
		}else if(_r['nn']=='1' && _ie.prop('tagName')=='INPUT' && (_ie.prop("type")=='checkbox' || _ie.prop("type")=='radio')){
			//单选框或复选框必选
			if(_ie.parents("form").find("input[name='"+_n+"']:checked").length==0){
				_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i> 必选项不能为空。</font>");
				if(keepfocus) _ie.focus();
				return false;
			}
		}else if(_r['nn']=='1' && _ie.prop('tagName')=='SELECT' && _ie.prop("multiple")){
			//列表多选
			if(_ie.find("option[selected]").length==0) {
				_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i> 必选项不能为空。</font>");
				if(keepfocus) _ie.focus();
				return false;
			}
		}
		
		//如果没有值或者没有选项，无需继续校验。
		if((!_v || _v == '') //没有填写值
			|| (_ie.prop('tagName')=='INPUT' && (_ie.prop("type")=='checkbox' || _ie.prop("type")=='radio')&& _ie.parents("form").find("input[name='"+_n+"']:checked").length == 0) //如果是复选框或单选框，没有选值
			|| (_ie.prop('tagName')=='SELECT' && _ie.prop("multiple") && _ie.find("option:selected").length==0)) //如果是SELECT多选没有选值
		{
			_ie.removeInputHint();
			return true;
		}
		
		
		
		//格式校验
		vt = _r['vt'];
		if(!vt) vt = 0;
		switch(vt){
			case 0:{
				if(_r['mx'] && parseInt(_r['mx'])<_v.length){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>已超出最大长度："+_r['mx']+",当前长度："+_v.length+"。</font>");
					if(keepfocus) _ie.focus();
					return false;
				}else if(_r['mn'] && parseInt(_r['mn'])>_v.length){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>没达到最小长度："+_r['mn']+",当前长度："+_v.length+"。</font>");
					if(keepfocus) _ie.focus();
					return false;
				}else
					_ie.removeInputHint();
				break;
			}
			case 1:{
				try{
					if(!(new RegExp("^(\\-)?\\d+$").test(_v))){
						_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>输入值不符合整数格式</font>");
						if(keepfocus) _ie.focus();
						return false;
					}else
						_v = parseInt(_v);
				}catch(err){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>输入值无法转换为整数</font>");
					if(keepfocus) _ie.focus();
					return false;
				}
				if(_r['mx'] && parseInt(_r['mx'])< _v){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>大于最大值["+_r['mx']+"]限制。</font>");
					if(keepfocus) _ie.focus();
					return false;
				}else if(_r['mn'] && parseInt(_r['mn'])> _v){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>小于最小值["+_r['mn']+"]限制。</font>"); 
					if(keepfocus) _ie.focus();
					return false;
				}else
					_ie.removeInputHint();
				break;
			}
			case 2:{
				try{
					if(!(new RegExp("^(\\-)?\\d+(\\.\\d+)?$").test(_v))){
						_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>输入值不符合浮点数格式</font>");
						if(keepfocus) _ie.focus();
						return false;
					}else
						_v = parseFloat(_v);
				}catch(err){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>输入值无法转换为浮点数</font>");
					if(keepfocus) _ie.focus();
					return false;
				}
				if(_r['mx'] && parseFloat(_r['mx'])< _v){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>大于最大值["+_r['mx']+"]限制。</font>");
					if(keepfocus) _ie.focus();
					return false;
				}else if(_r['mn'] && parseFloat(_r['mn'])> _v){
					_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>小于最小值["+_r['mn']+"]限制。</font>"); 
					if(keepfocus) _ie.focus();
					return false;
				}else
					_ie.removeInputHint();
				break;
			}
			case 3:{
				try{
					if(_r['rg'] && !(new RegExp(_r['rg']).test(_v))){
						_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>输入内容不符合格式要求。</font>");
						if(keepfocus) _ie.focus();
						return false;
					}else
						_ie.removeInputHint();
				}catch(err){
					console.log(err);
				}
				break;
			}
			case 5:{
				try{
					if(_r['rg'] && !(new RegExp(_r['rg'],"gi").test(_v))){
						_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>选择文件不符合要求。</font>");
						if(keepfocus) _ie.focus();
						return false;
					}else
						_ie.removeInputHint();
				}catch(err){
					console.log(err);
				}
				break;
			}
			case 4:{
				if(_ie.prop('tagName')=='INPUT' || _ie.prop('tagName')=='SELECT'){
					count =0;
					if(_ie.prop('tagName')=='INPUT' && _ie.prop("type")=='checkbox'){
						count = _ie.parents("form").find("input[name='"+_n+"']:checked").length;
						_mx = _ie.parents("form").find("input[name='"+_n+"']").length;
					}else if(_ie.prop('tagName')=='SELECT' && _ie.prop("multiple")){
						count = _ie.find("option:selected").length;
						_mx = _ie.find("option").length;
					}
					_mn = (_r['nn'] && _r['nn']==1)? 1:0; 
					if(_r['mx']) _mx = parseInt(_r['mx']);
					if(_r['mn']) _mn = parseInt(_r['mn']);
					
					if(!(_mx>= count && _mn<=count)){
						_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i> 只能选["+_mn+"至"+_mx+"]个选项。</font>");
						if(keepfocus) _ie.focus();
						return false;
					}else
						_ie.removeInputHint();
					
				}
				break;
			}
		}
		
		//返回JSON数据格式 {'code':'200(有效)/201(无效)','msg':'结果说明'}
		if(_r['rv']){
			m = getMenuByPermit(_r['rv']['pr']); //通过许可获取远程功能信息。
			if(m && m.url){
				utoken = sessionStorage.getItem("token");
				pass = true;
				data = _n+'='+_v+'&t='+new Date();
				//获取验证扩展参数的值。
				if(_r['rv']['ef']){
					for( key in _r['rv']['ef']){
						_ef = _ie.parents("form").findInputField(key);
						if(_ef.length==1 && ef.val()!=''){
							data +="&"+key+"="+ef.val();
						}else if(_ef.length>1 && _ef.find(":checked").length>1){
							for(i=0;i<_ef.find(":checked").length;i++){
								data +="&"+key+"="+_ef.find(":checked")[i].val();
							}
						}
					}
				}
				
				$.ajax({
				url:m.url,
				contentType:"charset=utf-8",
				data: data,
				dataType:"json",
				beforeSend: function(xhr){xhr.setRequestHeader('AUZ', utoken);},
				async: false, //同步请求
				success:function(res){
						if(res.code!=RESP_SUCCESS){
							_ie.inputHint("<font color=\"#f00\"><i class=\"fa fa-warning \"></i>"+res.msg+"</font>");
							if(keepfocus) _ie.focus();
							pass = false;
						}
					}
				});
				return pass;
			}
		}
		
	}
	return true;
}

/**
 * 给表单绑定验证
 */
jQuery.fn.formValidation = function(){
	if($(this).prop("tagName")=='FORM'){
		//绑定文本框，文件选择，下拉选择和文本域的失焦(blur)时验证
		$(this).on('blur',':text,:file,select,textarea',function(){$(this).validation(true)});
		//绑定复选框，单选框的点击(click)时验证
		$(this).on('click',':checkbox,:radio',function(){$(this).validation(true)});
	}
}

/**
 * 显示输入提示信息。
 */
jQuery.fn.inputHint = function(info){
	var _p = $(this).parents(".input-group");
	var pos = {left:$(_p).offset().left};
	var _i = _p.find(".input-hint");
	if(_i.length==0){
		_i = $("<div class=\"input-hint\"><div>");
		_p.append(_i).end();
		_i.offset(pos);
	}
	_i.html(info);
	
}
/**
 * 清除输入提示信息。
 */
jQuery.fn.removeInputHint = function(){
	_ih =$(this).parents(".input-group").find(".input-hint");
	if(_ih) $(_ih).remove();
}

